接下來幾篇文章以「在 Vue 3 Composition API 處理非同步( 發 API )」為主軸,會從新手的角度出發,告訴大家可以在哪些地方、時機發 API,會提到可能遇到的坑、該如何處理。
預計會整理 Vue 3 的 Lifecycle Hooks 和 Vue Router 的 Navigation Guard。
最後才會講到狀態管理器(Pinia),相信大家也會在過程中發現他的好用之處!
今天算是過渡篇章,會介紹 axios 基礎語法與設定,以及自己如何封裝管理 API,之後的文章都會直接用封裝好的寫法來處理!
行前須知 - API
在正式發 API 之前,我們要先來封裝 axios!
Promise based HTTP client for the browser and node.js
axios 是一個以 promise 為基礎的 HTTP 請求工具,可以用來傳送 HTTP 要求和接收 HTTP 回應,可以用在瀏覽器和 node.js 環境中,在瀏覽器環境中使用 XMLHttpRequest。
npm install axios
yarn add axios
<script src="https://cdn.jsdelivr.net/npm/axios/dist/axios.min.js"></script>
<script src="https://unpkg.com/axios/dist/axios.min.js"></script>
這樣就可以在專案中直接引用使用了!
import axios from "axios";
axios(config) //url 會寫在 config 裡面
axios(url[, config])
//也可以直接從 axios.method()
axios.get(url[, config])
axios.post(url[, data[, config]])
範例(使用 JSONPlaceholder):
//axios(config)
axios({
method: "get",
url: "https://jsonplaceholder.typicode.com/todos",
})
.then((response) => console.log(response.data))
.catch((error) => console.log(error));
//axios.get(url[, config])
axios
.get("https://jsonplaceholder.typicode.com/todos")
.then((response) => console.log(response.data))
.catch((error) => console.log(error));
常用 Request Config
get
PUT
、POST
、DELETE
和 PATCH
這幾個 HTTP 方法時可以用"https://challenge.thef2e.com/api/thef2e2019/stage6/"
,當 url 帶入 "/rooms"
,這次 request 的完整 url 就會是 "https://challenge.thef2e.com/api/thef2e2019/stage6/rooms"
Accept
: 客戶端可以接受的 response type (Client Request-header)Content-type
: 當下 request 或 response 的 payload 內容格式回傳值
axios 會回傳 Promise
.then
和 .catch
去處理成功和失敗的結果async
+await
搭配 try-catch 處理try {
const response = await axios({
method: "get",
url: "https://jsonplaceholder.typicode.com/todos",
});
console.log(response.data);
} catch (error) {
console.log(error);
}
如果對 Promise 和 async function 不熟悉,推薦下列文章:
在開發專案時,我們在處理 API 時,部份設定通常是相同的(例如:baseURL、header 等等),為了避免一直寫重複的程式碼,我們可以創建一個 axios 實例。
axios.create([config])
範例(使用六角學院旅館預約 API):
//創建實例
const hotelAPI = axios.create({
baseURL: "https://challenge.thef2e.com/api/thef2e2019/stage6",
headers: {
"Content-Type": "application/json; charset=utf-8",
Accept: "application/json",
},
});
//使用方法
const res = await hotelAPI.get("/rooms")
const res = await hotelAPI.delete("/rooms")
config 的部份可以參考上一段的常用 Request Config,這裡只重提上面範例用到的部份:
hotelAPI.get("/rooms")
,這次 request 的 url 就會是 "https://challenge.thef2e.com/api/thef2e2019/stage6/rooms"
Accept
: 客戶端可以接受的 response type (Client Request-header)Content-type
: 當下 request 或 response 的 payload 內容格式如果專案內用到不同 API,也可以創建多個 axios 實例來管理。
我們已經免去重複寫 config 的部份,還可以封裝不同的 request 方法,讓 API 在使用上更簡潔。
預期使用方式:
const data = await hotelAPI.GET("/rooms");
這裡主要示範封裝旅館預約 API 會用到的 GET、POST 跟 DELETE(使用全大寫是為了可以跟 axios 原本的 method 做區分),大家可以自己封裝其他會使用到的方法,或是需要特殊設定的 request,例如:登入、FormData 多媒體上傳。
export default {
async GET(url, params) {
try {
const res = await API.get(url, {
params,
});
return res.data;
} catch (res) {
return Promise.reject(res.message);
}
},
async DELETE(url, params) {
try {
const res = await API.delete(url, {
params,
});
return res.data;
} catch (res) {
return Promise.reject(res.message);
}
},
async POST(...arg) {
try {
const res = await API.post(...arg);
return res.data;
} catch (res) {
return Promise.reject(res.message);
}
},
};
透過 axios 攔截器,可以在送出 request
之前、收到 response
後,還沒進入 then()
或 catch()
處理之前,將一切動作攔截下,處理一些事情。
如果網站有會員登錄功能,在會員使用網站時,會觸發不同事件,過程中送出不同 API(如:查看購買清單、取消購買),而後端需要透過 token 驗證會員的身份,才會回傳相應的資料。
透過攔截器,我們可以在每次送出 request
之前,將請求攔截下來,將 token 加入 header,就能順利通過驗證,取得 API 資料。
可以在這裡攔截後端回傳的 response
,通常會針對常見的 HTTP 狀態碼做處理。
//request 攔截器
axios.interceptors.request.use(function (config) {
// 在送出 request 之前可以在這裡攔截處理
return config;
}, function (error) {
// 如果 request 出現 error
// 可以在這裡攔截處理
return Promise.reject(error);
});
//response 攔截器
axios.interceptors.response.use(function (response) {
// 回傳的 status code 在 2xx 區間會觸發這個函式
// 可以在這裡拿到 response 做處理
return response;
}, function (error) {
// 回傳的 status code 不在 2xx 區間會觸發這個函式
// 可以在這裡拿到 response error 做處理
return Promise.reject(error);
});
*補充 - status code:
這一段以六角 F2E 旅館預約 API 為例。
我們可以對剛剛建立的 axios 實例設定攔截器:
token
才可以順利拿到資料,所以我們可以在每次 request 之前,對 config 新增 Authorization 屬性,值為有效的 token。API.interceptors.request.use(
(config) => {
let access_token = 這裡放跟六角申請的 token
if (access_token) {
config.headers.Authorization = `Bearer ${access_token}`;
}
return config;
},
function (error) {
return Promise.reject(error);
}
);
API.interceptors.response.use(
function (response) {
return response;
},
function (error) {
if (error.response) {
switch (error.response.status) {
//可以在這裡針對不同 status code 做處理
case 401:
alert("token 無效");
console.log(error.message);
break;
case 404:
alert("頁面不存在");
console.log(error.message);
break;
case 500:
alert("程式發生問題");
console.log(error.message);
break;
default:
alert("程式發生問題");
console.log(error.message);
}
}
if (!window.navigator.onLine) {
alert("請重新連線後重整網頁");
return;
}
return Promise.reject(error);
},
);
今天就到這裡告一段落,之後幾篇會直接用封裝好的 axios 實例跟 method 來發 API,我們明天見拉~
封裝方式是參考饅頭大大的文章: